#include "ComputePipeline.h"
#include "../../Core/CoreGlobals.h"
#include "../../Utils/LogUtil.h"
#include "../../Templates/NewObject.h"
#include "../../Game/Process/RenderProcess.h"

ComputePipeline::Builder::Builder() noexcept = default;
ComputePipeline::Builder::~Builder() noexcept = default;
ComputePipeline::Builder::Builder(ComputePipeline::Builder const& rhs) noexcept = default;
ComputePipeline::Builder::Builder(ComputePipeline::Builder && rhs) noexcept = default;
ComputePipeline::Builder& ComputePipeline::Builder::operator=(ComputePipeline::Builder const& rhs) noexcept = default;
ComputePipeline::Builder& ComputePipeline::Builder::operator=(ComputePipeline::Builder && rhs) noexcept = default;

ComputePipeline::Builder& ComputePipeline::Builder::SetDescriptor(RefCountPtr<RHIDescriptor> descriptor) noexcept
{
	mImpl->descriptor = descriptor;
	return *this;
}

ComputePipeline::Builder& ComputePipeline::Builder::SetBasePipeline(RefCountPtr<RHIPipeline> basePipeline, int32 index /*= -1*/) noexcept
{
	mImpl->basePipelineHandle = basePipeline;
	mImpl->basePipelineIndex = index;
	return *this;
}

RefCountPtr<ComputePipeline> ComputePipeline::Builder::Build()
{
	assert(IsInRenderThread());
	return RefCountPtr<ComputePipeline>(NewObject<ComputePipeline>(*mImpl));
}

RefCountPtr<ComputePipeline> ComputePipeline::Builder::Build(RenderProcess* process, const std::string& name)
{
	assert(IsInRenderThread());
	ComputePipeline* computePipeline = NewObject<ComputePipeline>(*mImpl);
	process->AddPipeline(name, computePipeline);
	return RefCountPtr<ComputePipeline>(computePipeline);
}

void ComputePipeline::ReleaseResource()
{
	if (IsInRenderThread())
	{
		if (mProcess != nullptr)
		{
			mProcess->RemovePipeline(mName, this);
		}
		DeleteObject<ComputePipeline>(this);
	}
	else
	{
		AsyncTask(GRenderThreadId, [this]() {
			if (mProcess != nullptr)
			{
				mProcess->RemovePipeline(mName, this);
			}
			DeleteObject<ComputePipeline>(this);
			});
	}
}

ComputePipeline::ComputePipeline(BuilderDetails & builderDetails)
{
	mDescriptor = builderDetails.descriptor;

	VkComputePipelineCreateInfo pipelineCreateInfo{};
	pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
	pipelineCreateInfo.stage = mDescriptor->GetShaderStages()[0]->GetShaderStageCreateInfo();
	pipelineCreateInfo.layout = mDescriptor->GetPipelineLayout();

	if (builderDetails.basePipelineHandle.IsValid())
	{
		// 在基础管道之后创建的所有管道都将是衍生品
		pipelineCreateInfo.flags = VK_PIPELINE_CREATE_DERIVATIVE_BIT;
		// 基础管道将是我们创建的第一个管道
		pipelineCreateInfo.basePipelineHandle = builderDetails.basePipelineHandle->GetPipeline();
		// 只允许对基本管道使用句柄或索引
		// 当我们使用句柄时，我们必须将索引设置为 -1（参见规范的第 9.5 节）
		pipelineCreateInfo.basePipelineIndex = builderDetails.basePipelineIndex;
	}
	else
	{
		pipelineCreateInfo.basePipelineHandle = VK_NULL_HANDLE;
		pipelineCreateInfo.basePipelineIndex = -1;
	}

	VK_CHECK_RESULT(vkCreateComputePipelines(GVulkanRHI->GetLogicalDevice(), VK_NULL_HANDLE, 1, &pipelineCreateInfo, nullptr, &mPipeline))
}